/*
Copyright (c) 2010, Advanced Micro Devices
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions 
and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this list of 
conditions and the following disclaimer in the documentation and/or other materials provided 
with the distribution.

Neither the name of Advanced Micro Devices nor the names of its contributors may be used to endorse 
or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

/*! \file
*   \brief Lexer and Parser for Pipeline Kit    
*   \author Benedict R. Gaster 
*   
*   \version 0.1
*   \date March 2010
*/

#include "parser.h"
#include "pipeline-kit.hpp"

namespace amd {

	/*! \brief Utility function to calculate the size of a C style array.
	*
	* \param a is the array whose size will be calulated.
	*
	* \return size of array
	*/
	template<typename T, std::size_t z>
	static std::size_t arraySize(T const (&a)[z])
	{
		return z;
	}

	// Handle any static initializers

	// Keyword map setup.

	Parser::keywordPairType Parser::keywordInitializers_[] =
	{
		keywordPairType("false",     FALSETK),
		keywordPairType("true",      TRUETK),
		keywordPairType("Pipeline",  PIPELINE),
		keywordPairType("__global",  GLOBAL),
		keywordPairType("__local",   LOCAL),
		keywordPairType("Buffer",    BUFFER),
		keywordPairType("Image2D",   IMAGE2D),
		keywordPairType("Image3D",   IMAGE3D),
		keywordPairType("Uniform",   UNIFORM),
		keywordPairType("Kernel",    KERNEL),
		keywordPairType("Stage",     STAGE),
		keywordPairType("Graph",     GRAPH),
		keywordPairType( "input", INPUT ),
		keywordPairType( "output", OUTPUT ),
		keywordPairType( "inOut", INOUT ),
	}; 

	Parser::keywordType Parser::keywords_(
		keywordInitializers_, 
		keywordInitializers_ + arraySize(keywordInitializers_));

	/*!
	* \brief OpenCL C types supported for buffer definitions
	*
	* Note: currently limited to base types only
	*/
	std::string Parser::types_[] =
	{
		"bool",

		"char",	"char2", "char4", "char8", "char16",
		"uchar", "uchar2", "uchar4", "uchar8", "uchar16",

		"short", "short2", "short4", "short8", "short16",
		"ushort", "ushort2", "ushort4",	"ushort8", "ushort16",

		"int", "int2", "int4", "int8", "int16",
		"uint",	"uint2", "uint4", "uint8", "uint16",

		"long",	"long2", "long4", "long8", "long16", 
		"ulong", "ulong2", "ulong4", "ulong8", "ulong16",

		"float", "float2", "float4", "float8", "float16",

		"double", "double2", "double4",	"double8", "double16",

		"size_t",

		"void"
	};

	bool Parser::isType(std::string type)
	{
		bool found = false;

		for (std::size_t i = 0; i < arraySize(types_) && !found; i++) {
			if (types_[i].compare(type) == 0) {
				found = true;
			}
		}

		return found;
	}

	//------------------------------------------------------------------------------

	std::string Parser::stringifyToken(TokenType token)
	{
		std::string str;

		switch(token) {
	case NONE:
		str = "NONE";	
		break;
	case STRING:
		str = "STRING";
		break;
	case NAME:
		str = "NAME";	
		break;
	case NUMBER:
		str = "NUMBER";	
		break;
	case END:
		str = "END";	
		break;
	case NOT:
		str = "NOT";	
		break;
	case LHPAREN:
		str = "LHPAREN";	
		break;
	case RHPAREN:
		str = "RHPAREN";	
		break;
	case MULTIPLY:
		str = "MULTIPLY";	
		break;
	case PLUS:
		str = "PLUS";	
		break;
	case COMMA:
		str = "COMMA";	
		break;
	case MINUS:
		str = "MINUS";	
		break;
	case DIVIDE:
		str = "DIVIDE";	
		break;
	case COLON:
		str = "COLON";	
		break;
	case SEMI:
		str = "SEMI";	
		break;
	case LT:
		str = "LT";	
		break;
	case ASSIGN:
		str = "ASSIGN";	
		break;
	case GT:	
		str = "GT";	
		break;
	case LE:
		str = "LE";	
		break;
	case GE:
		str = "GE";	
		break;
	case EQ:
		str = "EQ";	
		break;
	case NE:
		str = "NE";	
		break;
	case AND:
		str = "AND";	
		break;
	case OR:
		str = "OR";	
		break;
	case ARROW:
		str = "ARROW";	
		break;
	case PIPELINE:
		str = "PIPELINE";
		break;
	case GLOBAL:
		str = "GLOBAL";	
		break;
	case LOCAL:
		str = "LOCAL";	
		break;
	case BUFFER:
		str = "BUFFER";	
		break;
	case IMAGE2D:
		str = "IMAGE2D";	
		break;
	case UNIFORM:
		str = "UNIFORM";	
		break;
	case STAGE:
		str = "STAGE";	
		break;
	case GRAPH:
		str = "GRAPH";	
		break;
	case CODECL:
		str = "CODECL";	
		break;
	case CODECPP:
		str = "CODECPP";	
		break;
	default:
		throw std::runtime_error("invalid token: " + token);
		}

		return str;
	}

	const void Parser::skipWhiteSpaceComments(void)
	{
		// skip spaces
		while (*pWord_ && isspace (*pWord_)) {
			pWord_++;
		}

		// C++ style comments
		if (*pWord_ == '/' && *(pWord_+1) == '/') {
#if defined(_WIN32)
			while (*pWord_ && *pWord_ != '\r' && *(pWord_+1) != '\n') {
#else
			while (*pWord_ && *pWord_ != '\n') {
#endif
				pWord_++;
			}

#if defined(_WIN32)
			pWord_++;
#endif

			skipWhiteSpaceComments();
		}
	}

	const Parser::TokenType Parser::getToken (const bool ignoreSign)
	{
		word_.erase (0, std::string::npos);

		skipWhiteSpaceComments();

		pWordStart_ = pWord_;   // remember where word_ starts *now*

		// look out for unterminated statements and things
		if (*pWord_ == 0 && type_ == END) {  
			throw std::runtime_error ("Unexpected end of expression.");
		}

		unsigned char cFirstCharacter = *pWord_;        // first character in new word_

		// stop at EOF
		if (cFirstCharacter == 0)  {
			word_ = "<end of expression>";
			type_ = END;
			return type_;
		}

		if( cFirstCharacter == '"' ){
			static char buffer[ 1024 ];
			memset( buffer, 0, sizeof( buffer ));
			char * b = buffer;
			pWord_++;
			while( *pWord_ != '"' && ( b - buffer < sizeof( buffer )) ){
				*b++ = *pWord_++; 
			}
			pWord_++;
			word_ = buffer;
			type_ = STRING;
			return type_;
		}

		unsigned char cNextCharacter  = *(pWord_ + 1);  // 2nd character in new word_

		// look for number
		// can be: + or - followed by a decimal point
		// or: + or - followed by a digit
		// or: starting with a digit
		// or: decimal point followed by a digit
		if ((!ignoreSign &&
			(cFirstCharacter == '+' || cFirstCharacter == '-') &&
			(isdigit (cNextCharacter) || cNextCharacter == '.'))
			|| isdigit (cFirstCharacter)
			// allow decimal numbers without a leading 0. e.g. ".5"
			|| (cFirstCharacter == '.' && isdigit (cNextCharacter)) ) {

				// skip sign for now
				if ((cFirstCharacter == '+' || cFirstCharacter == '-')) {
					pWord_++;
				}

				while (isdigit (*pWord_) || *pWord_ == '.') {
					pWord_++;
				}

				// allow for 1.53158e+15
				if (*pWord_ == 'e' || *pWord_ == 'E') {
					pWord_++; // skip 'e'

					if ((*pWord_  == '+' || *pWord_  == '-')) {
						pWord_++; // skip sign after e
					}

					while (isdigit (*pWord_))  { // now digits after e
						pWord_++;      
					}
				}

				word_ = std::string (pWordStart_, pWord_ - pWordStart_);

				std::istringstream is (word_);
				// parse std::string into double value
				is >> value_;

				if (is.fail () || !is.eof ()) {
					throw std::runtime_error ("Bad numeric literal: " + word_);
				}

				type_ = NUMBER;
				return type_;
		}

		// special test for 2-character sequences: <= >= == !=
		// also +=, -=, /=, *=
#if 0
		if (cNextCharacter == '=')
		{
			switch (cFirstCharacter)
			{
				// comparisons
			case '=': type_ = EQ;   break;
			case '<': type_ = LE;   break;
			case '>': type_ = GE;   break;
			case '!': type_ = NE;   break;
				// assignments
			case '+': type_ = ASSIGN_ADD;   break;
			case '-': type_ = ASSIGN_SUB;   break;
			case '*': type_ = ASSIGN_MUL;   break;
			case '/': type_ = ASSIGN_DIV;   break;
				// none of the above
			default:  type_ = NONE; break;
			} // end of switch on cFirstCharacter

			if (type_ != NONE)
			{
				word_ = std::string (pWordStart_, 2);
				pWord_ += 2;   // skip both characters
				return type_;
			} // end of found one    
		} // end of *=
#endif 
		// special test for "->" sequence
		if (cFirstCharacter == '-' && cNextCharacter == '>') {
			word_ = std::string (pWordStart_, 2);
			pWord_ += 2;
			type_ = ARROW;
			return type_;
		}

		switch (cFirstCharacter) {
	case '&': 
		if (cNextCharacter == '&') {
			word_ = std::string (pWordStart_, 2);
			pWord_ += 2;
			type_ = AND;
			return type_;
		}
		break;
	case '|': 
		if (cNextCharacter == '|') {
			word_ = std::string (pWordStart_, 2);
			pWord_ += 2;   // skip both characters
			type_ = OR;
			return type_;
		}
		break;
		// single-character symboles
	case '=':
	case '<':
	case '>':
	case '+':
	case '-':
	case '/':
	case '*':
	case '(':
	case ')':
	case ',':
	case '!':
	case ';':
	case ':':
	case '{':
	case '}':
		word_ = std::string (pWordStart_, 1);
		pWord_++;
		type_ = TokenType (cFirstCharacter);
		return type_;
		}

		if (!isalpha (cFirstCharacter) && cFirstCharacter != '_' ) {
			if (cFirstCharacter < ' ') {
				std::ostringstream s;
				s << "Unexpected character (decimal " << int (cFirstCharacter) << ")";
				throw std::runtime_error (s.str ());    
			}
			else {
				throw std::runtime_error (
					"Unexpected character: " + std::string (1, cFirstCharacter));
			}
		}

		// we have a word (starting with A-Z)
		while (isalnum (*pWord_) || *pWord_ == '_') {
			pWord_++;
		}

		word_ = std::string (pWordStart_, pWord_ - pWordStart_);

		// Is it a code section, i.e. CodeC or CodeCL
		if((word_.compare("CodeCPP") == 0) || (word_.compare("CodeCL") == 0)) {
			type_ = word_.compare("CodeCPP") ? CODECPP : CODECL;

			// skip spaces
			while (*pWord_ && isspace (*pWord_)) {
				pWord_++;
			}

			// look out for unterminated statements and things
			if (*pWord_ == 0) {  
				throw std::runtime_error ("Unexpected end of expression.");
			}

			if(*pWord_ != '{') {
				throw std::runtime_error ("expected {");
			}

			pWord_++;
			pWordStart_ = pWord_;   // remember where code section starts

			while (*pWord_ != 0 && *pWord_ != '}') {
				pWord_++;
			}

			if(*pWord_ == 0 || *pWord_ != '}') {
				throw std::runtime_error ("expected }");
			}
			pWord_++;

			word_ = std::string (pWordStart_, (pWord_ - 1) - pWordStart_);
			return type_;
		}

		// is it a keyword
		keywordType::iterator keyword = keywords_.find(word_);
		if (keyword != keywords_.end()) {
			type_ = (*keyword).second;
		}
		else {
			type_ = NAME;
		}
		return type_;
	}   

	//----------------------------------------------------------------------------------

	// Utility parsers

	bool Parser::given(TokenType type, const bool getTok)
	{
		if (getTok) {
			getToken();
		}

		return (type_ == type);
	}

	//----------------------------------------------------------------------------------

	ASTExpr Parser::atomic(const bool getTok)
	{
		ASTExpr expr;

		if (getTok) {
			getToken();
		}

		switch(type_) {
		case STRING:
			expr = ASTExpr( word_ );
			getToken();
			break;

		case NUMBER:
			expr = ASTExpr(value_);
			getToken();
			break;

		case TRUETK:
		case FALSETK:
		case NAME:
			expr = ASTExpr(word_);
			getToken();
			break;

		case LHPAREN:
			expr = expression(true);
			checkToken(RHPAREN);
			getToken();
			break;
		default:
			throw std::runtime_error("(atomic) Unexpeted token: " + word_);
		}

		return expr;
	}

	ASTExpr Parser::term(const bool getTok)
	{
		ASTExpr left = atomic(getTok);
		if( type_ == STRING ){
			return left;
		}
		bool exitLoop = false;

		while(!exitLoop) {
			switch (type_) {
		case MULTIPLY:
			{
				ASTExpr right = atomic(true);
				left = ASTExpr(ASTBinaryOp<AST_MULTIPLY>(left,right));
				break;
			}
		case DIVIDE:
			{
				ASTExpr right = atomic(true);
				left = ASTExpr(ASTBinaryOp<AST_DIVIDE>(left,right));
				break;
			}
		default: 
			exitLoop = true;
			break;
			}
		}

		return left;
	}

	ASTExpr Parser::expression(const bool getTok)
	{
		ASTExpr left = term(getTok);
		if( type_ == STRING ){
			return left;
		}
		bool exitLoop = false;

		while(!exitLoop) {
			switch (type_) {
		case PLUS:
			{
				ASTExpr right = term(true);
				left = ASTExpr(ASTBinaryOp<AST_PLUS>(left,right));
				break;
			}
		case MINUS:
			{
				ASTExpr right = term(true);
				left = ASTExpr(ASTBinaryOp<AST_PLUS>(left,right));
				break;
			}
		default: 
			exitLoop = true;
			break;
			}
		}

		return left;
	}

	//-----------------------------------------------------------------------------

	ASTAttributeList Parser::attributes(const bool getTok, const bool named)
	{
		ASTAttributeList list;
		bool exitLoop = false;

		if (!given(LHBRACE, getTok)) {
			throw std::runtime_error("(attribute) expecting {");
		}

		while(!exitLoop) {
			ASTAttribute attr;
			bool readToken = true;

			if (!given(NAME)) {
				exitLoop = true;
			}
			else {
				std::string str = word_;

				if (named) {
					if (given(COLON)) {
						boost::get<0>(attr) = str;

						if (!given(NAME)) {
							throw std::runtime_error("(attribute) expecting name");
						}
						str = word_;
					}
					else {
						readToken = false;
					}
				}

				boost::get<1>(attr) = str;

				if (!given(ASSIGN, readToken)) {
					throw std::runtime_error("(attribute) expecting =");
				}

				boost::get<2>(attr) = expression();

				if (!given(SEMI, false)) {
					throw std::runtime_error("(attribute) expecting ;");
				}

				list.push_back(attr);
			}
		}

		if (!given(RHBRACE, false)) {
			throw std::runtime_error("(attribute) expecting }");
		}

		return list;
	}

	//-----------------------------------------------------------------------------

	/*!
	* \brief Parser image2D definition
	*
	* Syntax:
	*
	*	Image2D Name(address_space,type,sizex,sizey) { attributes }
	*/
	ASTImage2D Parser::image2D(const bool getTok)
	{
		ASTImage2D image2D;

		if (!given(NAME, getTok)) {
			throw std::runtime_error("(image2D) missing name");
		}

		image2D.name_ = word_;

		if (!given(LHPAREN)) {
			throw std::runtime_error("(image2D) expecting (");
		}

		if (given(GLOBAL)) {
			image2D.aspace_ = AST_GLOBAL;
		}
		else if (given(LOCAL,false)) {
			image2D.aspace_ = AST_LOCAL;
		}
		else {
			throw std::runtime_error(
				"(image2D) unknown address space " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(image2D) expecting comma " + word_);
		}

		if (given(NAME)) {
			if (isType(word_)) {
				image2D.type_ = word_;
			}
		}
		else {
			throw std::runtime_error(
				"(image2D) expecting a type " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(image2D) expecting comma " + word_);
		}

		// size
		image2D.sizeX_ = expression();

		image2D.sizeY_ = expression();

		if (!given(RHPAREN, false)) {
			throw std::runtime_error("(image2D) expecting )");
		}

		// attributes
		image2D.attrs_ = attributes();

		return image2D;
	}


	/*!
	* \brief Parser image3D definition
	*
	* Syntax:
	*
	*	Image3D Name(address_space,type,sizex,sizey) { attributes }
	*/
	ASTImage3D Parser::image3D(const bool getTok)
	{
		ASTImage3D image3D;

		if (!given(NAME, getTok)) {
			throw std::runtime_error("(image3D) missing name");
		}

		image3D.name_ = word_;

		if (!given(LHPAREN)) {
			throw std::runtime_error("(image3D) expecting (");
		}

		if (given(GLOBAL)) {
			image3D.aspace_ = AST_GLOBAL;
		}
		else if (given(LOCAL,false)) {
			image3D.aspace_ = AST_LOCAL;
		}
		else {
			throw std::runtime_error(
				"(image3D) unknown address space " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(image3D) expecting comma " + word_);
		}

		if (given(NAME)) {
			if (isType(word_)) {
				image3D.type_ = word_;
			}
		}
		else {
			throw std::runtime_error(
				"(image3D) expecting a type " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(image3D) expecting comma " + word_);
		}

		// size
		image3D.sizeX_ = expression();

		image3D.sizeY_ = expression();

		image3D.sizeZ_ = expression();

		if (!given(RHPAREN, false)) {
			throw std::runtime_error("(image3D) expecting )");
		}

		// attributes
		image3D.attrs_ = attributes();

		return image3D;
	}

	//-----------------------------------------------------------------------------

	/*!
	* \brief Parse buffer definition
	*
	* Syntax:
	*
	*	Buffer Name(address_space,type,size) { attributes }
	*/
	ASTBuffer Parser::buffer(const bool getTok)
	{
		ASTBuffer buffer;

		if (!given(NAME, getTok)) {
			throw std::runtime_error("(buffer) missing name");
		}

		buffer.name_ = word_;

		if (!given(LHPAREN)) {
			throw std::runtime_error("(buffer) expecting (");
		}

		if (given(GLOBAL)) {
			buffer.aspace_ = AST_GLOBAL;
		}
		else if (given(LOCAL,false)) {
			buffer.aspace_ = AST_LOCAL;
		}
		else {
			throw std::runtime_error(
				"(buffer) unknown address space " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(buffer) exprectting comma " + word_);
		}

		if (given(NAME)) {
			if (isType(word_)) {
				buffer.type_ = word_;
			}
		}
		else {
			throw std::runtime_error(
				"(buffer) expecting a type " + word_);
		}

		if (!given(COMMA)) {
			throw std::runtime_error(
				"(buffer) exprectting comma " + word_);
		}

		// size
		buffer.size_ = expression();

		if (!given(RHPAREN, false)) {
			throw std::runtime_error("(buffer) expecting )");
		}

		// attributes
		buffer.attrs_ = attributes();

		return buffer;
	}

	Maybe<ASTMemory>::MaybeType Parser::memory(const bool getTok)
	{
		Maybe<ASTMemory>::MaybeType mem;

		if(getTok) {
			getToken();
		}

		switch(type_)
		{
		case BUFFER:
			mem = Maybe<ASTMemory>::MaybeType(buffer(true));
			break;
		case IMAGE2D:
#if 0
			// no support for images yet
			throw std::runtime_error("(memory) 2D images not yet supported");
#else
			mem = Maybe<ASTMemory>::MaybeType( image2D( true ));
#endif
			break;
		case IMAGE3D:
#if 0
			throw std::runtime_error("(memory) 3D images not yet supported");
#else
			mem = Maybe<ASTMemory>::MaybeType( image3D( true ));
#endif
			break;
		default:
			mem = Maybe<ASTMemory>::MaybeType(
				boost::tuple<std::string>("not a memory object"));
			break;
		}

		return mem;
	}

	//------------------------------------------------------------------------------

	bool Parser::environment(ASTVarDefMap& env, const bool getTok)
	{
		bool parsed = false;

#if 0//old syntax
		if (given(NAME, getTok) && isType(word_)) {
#else//new syntax
		//if( true ){
		if (given(NAME, getTok)){
#endif
			ASTVarDef vardef;
#if 0//old syntax
			boost::get<0>(vardef) = word_;
#endif

			std::string name = word_;

#if 0//old syntax
			if(given(NAME)) {
				std::string name = word_;
#else//new syntax
			if( true ){
#endif

				if (given(ASSIGN)) {
					boost::get<1>(vardef) = expression();

					if (!given(SEMI, false)) {
						throw std::runtime_error(
							"(environment) expecting ';'");
					}

					env[name] = vardef;

					parsed = true;
				}
				else {
#if 0//new syntax
					if (!given(SEMI, false)) {
						throw std::runtime_error(
							"(environment) expecting ';'");
					}
					boost::get<1>(vardef) = "undefined";
					env[name] = vardef;
					parsed = true;
#else
					throw std::runtime_error(
						"(environment) expecting '='");
#endif
				}
			}
			else {
				throw std::runtime_error(
					"(environment) expecting name");
			}
		}

		return parsed;
	}

	//------------------------------------------------------------------------------

	Maybe<ASTCode>::MaybeType Parser::code(const bool getTok)
	{
		Maybe<ASTCode>::MaybeType mcode;

		if (given(CODECL, getTok) || given(CODECPP, false)) 
		{
			ASTCode code;
			code.code_ = word_;

			switch(type_) {
			case CODECL:
				code.type_ = AST_CODECL;
				break;
			case CODECPP:
				code.type_ = AST_CODECPP;
				break;
			default:
				throw std::runtime_error("(code) unsupported code type");
			}

			// attributes
			code.attrs_ = attributes();

			mcode = Maybe<ASTCode>::MaybeType(code);
		}
		else {
			mcode = Maybe<ASTCode>::MaybeType(
				boost::tuple<std::string>("not a code section"));
		}

		return mcode;
	}

	//------------------------------------------------------------------------------

	Maybe<ASTUniform>::MaybeType Parser::uniform(const bool getTok)
	{
		Maybe<ASTUniform>::MaybeType muniform;

		ASTUniform uniform;

		if (!given(UNIFORM, getTok)) {
			muniform = Maybe<ASTUniform>::MaybeType(
				boost::tuple<std::string>("not a uniform definition"));
		}
		else {
			if (!given(NAME) && isType(word_)) {
				throw std::runtime_error("(uniform) missing type");
			}

			uniform.type_ = word_;

			if (given(NAME)) {
				uniform.name_ = word_;

				// attributes
				uniform.attrs_ = attributes();

				muniform = Maybe<ASTUniform>::MaybeType(uniform);
			}
			else {
				throw std::runtime_error("(uniform) missing name");
			}
		}


		return muniform;
	}

	//------------------------------------------------------------------------------

	ASTTypeVarList Parser::kernelArguments(const bool getTok)
	{
		ASTTypeVarList list;
		bool exitLoop = false;

		if (!given(LHPAREN, getTok)) {
			throw std::runtime_error("(kernelArguments) expecting (");
		}

		while(!exitLoop) {
			ASTTypeVarElement arg;
			bool readToken = true;
			bool isImage = false;

			if( given( IMAGE2D )){
				boost::get< 3 >( arg ) = IMAGE2D;
				isImage = true;
			}
			else if( given( IMAGE3D, false )){
				boost::get< 3 >( arg ) = IMAGE3D;
				isImage = true;
			}
			else if ( given( GLOBAL, false )) {
				boost::get<0>(arg) = AST_GLOBAL;
				boost::get< 3 >( arg ) = BUFFER;
			}
			else if ( given( LOCAL, false )) {
				boost::get<0>(arg) = AST_LOCAL;
				boost::get< 3 >( arg ) = BUFFER;
			}
			else {
				boost::get<0>(arg) = AST_UNIFORM;
				boost::get< 3 >( arg ) = UNIFORM;
				readToken = false;
			}

			if (given(NAME, readToken)){// && isType(word_)) {

				if( !isImage && isType( word_ )){
					boost::get<1>(arg) = word_;
					if (!given(NAME)) {
						throw std::runtime_error("(kernelArguments) expecting name");
					}
				}

				boost::get<2>(arg) = word_;//VarName

				if( given( INPUT, true ) || given( OUTPUT, false ) || given( INOUT, false )){
					boost::get<4>( arg ) = word_;//IoClass
					list.push_back(arg);
				}
				else{
				}


				if(!given(COMMA)) {
					exitLoop = true;
				}
			}
			else {
				exitLoop = true;
			}
		}

		if (!given(RHPAREN, false)) {
			throw std::runtime_error("(kernelArguments) expecting )");
		}

		return list;
	}


	Maybe<ASTKernel>::MaybeType Parser::kernel(const bool getTok)
	{
		Maybe<ASTKernel>::MaybeType mkernel;

		ASTKernel kernel;

		if (!given(KERNEL, getTok)) {
			mkernel = Maybe<ASTKernel>::MaybeType(
				boost::tuple<std::string>("not a kernel definition"));
		}
		else {
			if (!given(NAME)) {
				throw std::runtime_error("(kernel) missing name");
			}

			kernel.name_ = word_;

			if (!given(COLON)) {
				throw std::runtime_error("(kernel) expecting :");
			}

#if 1//new
			kernel.arguments_ = kernelArguments();
#else
			kernel.inputs_ = kernelArguments();

			if (!given(ARROW)) {
				throw std::runtime_error("(kernel) expecting ->");
			}

			kernel.outputs_ = kernelArguments();
#endif

			// attributes
			kernel.attrs_ = attributes(true, true);

			mkernel = Maybe<ASTKernel>::MaybeType(kernel);
		}

		return mkernel;
	}

	//------------------------------------------------------------------------------

	ASTTypeList Parser::stageArguments(const bool getTok)
	{
		ASTTypeList list;
		bool exitLoop = false;

		if (!given(LHPAREN, getTok)) {
			throw std::runtime_error("(stageArguments) expecting (");
		}

		while(!exitLoop) {
			if (given(NAME)) {
				std::string str = word_;
				list.push_back(str);
			}
			else if( given( COMMA, false )){
				continue;
			}
			else {
				exitLoop = true;
			}
		}//while

		if (!given(RHPAREN, false)) {
			throw std::runtime_error("(stageArguments) expecting )");
		}

		return list;
	}

	Maybe<ASTStage>::MaybeType Parser::stage(const bool getTok)
	{
		Maybe<ASTStage>::MaybeType mstage;

		ASTStage stage;

		if (!given(STAGE, getTok)) {
			mstage = Maybe<ASTStage>::MaybeType(
				boost::tuple<std::string>("not a stage definition"));
		}
		else {
			if (!given(NAME)) {
				throw std::runtime_error("(stage) missing name");
			}

			stage.name_ = word_;

			if (!given(COLON)) {
				throw std::runtime_error("(stage) expecting :");
			}

			if (!given(NAME)) {
				throw std::runtime_error("(stage) missing kernel nameor 'dummy'");
			}

			stage.kernelName_ = word_;

#if 1//new
			stage.args_ = stageArguments();

#else//obsolete
			stage.readonlyArgs_ = stageArguments();

			if (!given(ARROW)) {
				throw std::runtime_error("(stage) expecting ->");
			}

			stage.readwriteArgs_ = stageArguments();
#endif

			// attributes
			stage.attrs_ = attributes(true, true);

			mstage = Maybe<ASTStage>::MaybeType(stage);
		}

		return mstage;
	}

	//------------------------------------------------------------------------------

	Maybe<ASTGraph>::MaybeType Parser::graph(const bool getTok)
	{
		Maybe<ASTGraph>::MaybeType gstage;

		ASTGraph graph;

		if (!given(GRAPH, getTok)) {
			gstage = Maybe<ASTGraph>::MaybeType(
				boost::tuple<std::string>("not a graph definition"));
		}
		else {
			ASTGraphEdge edge;
			bool exitLoop = false;

			if (!given(NAME)) {
				throw std::runtime_error("(graph) missing name");
			}

			graph.name_ = word_;

			if (!given(LHBRACE)) {
				throw std::runtime_error("(graph) missing {");
			}

			do {
				if (!given(NAME)) {
					exitLoop = true;
				}
				else {
					boost::get<0>(edge) = word_;

					if (!given(COMMA)) {
						throw std::runtime_error("(graph) expecting ,");
					}

					if (!given(NAME)) {
						throw std::runtime_error("(graph) missing edge");
					}

					boost::get<1>(edge) = word_;

					if (!given(SEMI)) {
						throw std::runtime_error("(graph) expecting ;");
					}

					graph.edges_.push_back(edge);
				}
			} while (!exitLoop);

			if (!given(RHBRACE, false)) {
				throw std::runtime_error("(graph) missing }");
			}

			graph.attrs_ = attributes();

			gstage = Maybe<ASTGraph>::MaybeType(graph);
		}

		return gstage;
	}

	//------------------------------------------------------------------------------

	ASTPipeline Parser::pipeline(const bool getTok)
	{
		bool exitLoop = true;
		ASTPipeline pline;
		bool readToken = false;

		if(!given(PIPELINE)) {
			throw std::runtime_error(
				"(pipeline) expected Pipeline but got" + word_);
		}

		if (!given(NAME)) {
			throw std::runtime_error("(pipeline) expecting name");
		}
		pline.name_ = word_;

		pline.attrs_ = attributes();

		if (!given(LHBRACE)) {
			throw std::runtime_error("(pipeline) expecting {");
		}

		// parse any code sections
		exitLoop = false;
		do {
			Maybe<ASTCode>::MaybeType codesec = code();

			if (Maybe<ASTCode>::toBool(codesec)) {
				pline.codes_.push_back(Maybe<ASTCode>::toJust(codesec));
			}
			else {
				exitLoop = true;
			}
		} while (!exitLoop);


#if 0//OLD SYNTAX

		// parse any variable definitions
		exitLoop = false;
		readToken = false;
		do {
			if (environment(pline.environment_, readToken)) {
				readToken = true;
			}
			else {
				exitLoop = true;
			}
		} while (!exitLoop);

		// parse any memory definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTMemory>::MaybeType mem = memory(readToken);
			readToken = true;

			if (Maybe<ASTMemory>::toBool(mem)) {
				pline.memorys_.push_back(Maybe<ASTMemory>::toJust(mem));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

		// parse any uniform definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTUniform>::MaybeType uni = uniform(readToken);
			readToken = true;

			if (Maybe<ASTUniform>::toBool(uni)) {
				pline.uniforms_.push_back(Maybe<ASTUniform>::toJust(uni));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

#else//NEW SYNTAX

		// parse any uniform definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTUniform>::MaybeType uni = uniform(readToken);
			readToken = true;

			if (Maybe<ASTUniform>::toBool(uni)) {
				pline.uniforms_.push_back(Maybe<ASTUniform>::toJust(uni));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

		// parse any variable definitions
		exitLoop = false;
		readToken = false;
		do {
			if (environment(pline.environment_, readToken)) {
				readToken = true;
			}
			else {
				exitLoop = true;
			}
		} while (!exitLoop);

		// parse any memory definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTMemory>::MaybeType mem = memory(readToken);
			readToken = true;

			if (Maybe<ASTMemory>::toBool(mem)) {
				pline.memorys_.push_back(Maybe<ASTMemory>::toJust(mem));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

#endif


		// parse any kernel definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTKernel>::MaybeType lkernel = kernel(readToken);
			readToken = true;

			if (Maybe<ASTKernel>::toBool(lkernel)) {
				pline.kernels_.push_back(Maybe<ASTKernel>::toJust(lkernel));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

		// parse any stage definitions
		exitLoop = false;
		readToken = false;
		do {
			Maybe<ASTStage>::MaybeType lstage = stage(readToken);
			readToken = true;

			if (Maybe<ASTStage>::toBool(lstage)) {
				pline.stages_.push_back(Maybe<ASTStage>::toJust(lstage));
			}
			else {
				exitLoop = true;
			}

		} while (!exitLoop);

		// parse graph definitions
		readToken = false;
		Maybe<ASTGraph>::MaybeType lgraph = graph(readToken);
		if (Maybe<ASTGraph>::toBool(lgraph)) {
			pline.graph_ = Maybe<ASTGraph>::toJust(lgraph);
		}

		if (!given(RHBRACE, false)) {
			throw std::runtime_error("(pipeline) expecting }");
		}

		return pline;
	}

}; // namespace amd 